﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using SevenZip;

namespace picomp
{
    internal static class Utilities
    {
        public static void Breathe()
        {
            var currentProcess = Process.GetCurrentProcess();
            var memUsed = (int) currentProcess.WorkingSet64/(1024*1024);
            if (memUsed > 32)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
                Thread.Sleep(10);
            }
        }

        public static string AssemblyDirectory()
        {
            var codeBase = Assembly.GetExecutingAssembly().CodeBase;
            var uri = new UriBuilder(codeBase);
            var path = Uri.UnescapeDataString(uri.Path);
            return Path.GetDirectoryName(path);
        }

        public static bool IsArchive(string filename)
        {
            var ext = Path.GetExtension(filename).ToLower();
            return (ext == @".rar") || (ext == @".zip") || (ext == @".7z");
        }

        public static IEnumerable<string> GetZipArchives(string path)
        {
            var queue = new Queue<string>();
            queue.Enqueue(path);
            while (queue.Count > 0)
            {
                path = queue.Dequeue();
                try
                {
                    foreach (var subDir in Directory.GetDirectories(path))
                    {
                        queue.Enqueue(subDir);
                    }
                }
                catch (Exception ex)
                {
                    Console.Error.WriteLine(ex);
                }

                string[] files = null;
                try
                {
                    files = Directory.GetFiles(path);
                }
                catch (Exception ex)
                {
                    Console.Error.WriteLine(ex);
                }

                if (files != null)
                {
                    foreach (var t in files.Where(IsArchive))
                    {
                        yield return t;
                    }
                }
            }
        }

        public static string GetTempFilePath(string folder, string prefix, string extension)
        {
            var fileName = Path.ChangeExtension(prefix + Guid.NewGuid().ToString("N").Substring(0, 4).ToLower(),
                                                extension);
            return Path.Combine(folder, fileName);
        }

        public static string RandString(int length = 4)
        {
            return Guid.NewGuid().ToString("N").Substring(0, length).ToLower();
        }

        public static void CopyStream(Stream input, Stream output)
        {
            var buffer = new byte[64*1024];
            int len;
            while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, len);
            }
        }

        public static string LocateFirstVolume(string filename)
        {
            bool isVolume;
            uint parts;

            using (var extractor = new SevenZipExtractor(filename))
            {
                isVolume =
                    extractor.ArchiveProperties.Any(x =>
                                                        x.Name.Equals("IsVolume") && x.Value.Equals(true));

                parts = (
                    from x in extractor.ArchiveProperties
                    where x.Name.Equals("Number of volumes")
                    select (uint) x.Value).DefaultIfEmpty(1u).SingleOrDefault();
            }

            if (!isVolume)
                return null;

            if (parts > 1)
                return filename;

            if (!Path.GetExtension(filename).Equals(".rar", StringComparison.OrdinalIgnoreCase))
            {
                var rarFile = Path.Combine(Path.GetDirectoryName(filename),
                                           Path.GetFileNameWithoutExtension(filename) + ".rar");

                if (File.Exists(rarFile))
                {
                    var firstVolume = LocateFirstVolume(rarFile);

                    if (firstVolume != null)
                    {
                        return firstVolume;
                    }
                }
            }

            var directoryFiles = Directory.GetFiles(Path.GetDirectoryName(filename));

            foreach (var directoryFile in directoryFiles)
            {
                var firstVolume = LocateFirstVolume(directoryFile);

                if (firstVolume != null)
                {
                    using (var extractor = new SevenZipExtractor(firstVolume))
                    {
                        if (extractor.VolumeFileNames.Contains(filename))
                        {
                            return firstVolume;
                        }
                    }
                }
            }

            return null;
        }
    }
}